import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/*
* <T>  此时代表当前类 是一个泛型类，T：当做是一个占位符
* 通俗来说：<T> 代表下一次 给类传参的 数据类型。
* 或者，规定了下一次参数的类型是什么。
*
* 那么，下面的代码也要做出修整
* */
class  MyArray<T extends Number>{
    // 假设这行代码可以运行： public T[] objects = new T[10];
    public  T[] objects = (T[])new Object[10];

    // 设置指定下标的元素值
    public void set(int pos,T val){
        objects[pos] = val;
    }

    // 返回指定下标的元素值
    public T get(int pos){
        return objects[pos];
    }
    // 获取数组 : 相当于返回的数组类型 Object 类型的数组
    public T[] getArray(){
        return objects;
    }
}

// 写一个泛型类，求出数组当中的最大值
class Alg<T>{
    public static<T extends Comparable<T>> T findMax(T[] array){
        T max  = array[0];
        for(int i = 0;i < array.length; i++){
            if(max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }
}
class Alg1<T>{
    public static<T> void print1(ArrayList<T> list){
        for (T x: list) {
            System.out.println(x);
        }
    }

    public static void print2(ArrayList<?> list){
        for (Object x:list) {
            System.out.println(x);
        }
    }


}

class A{

}
class Person extends A{

}
class Student extends Person{

}


public class Genericity {
    public static void main(String[] args) {
        ArrayList<? super Person> arrayList1 = new ArrayList<Student>();
        // super 规定了 通配符的下界，也就说 arrayList1 只能引用 person 或者 person的父类，
        // person的子类不可以。

        ArrayList<? super Person> arrayList2 = new ArrayList<A>();
        arrayList2.add(new Person());
        arrayList2.add(new Student());
        arrayList2.add(new A());

        ArrayList<? super Person> arrayList = new ArrayList<Person>();
        arrayList.add(new Person());
        arrayList.add(new Student());
        arrayList.add(new A());
        // 有人可能说：不是说只能接收 Person 或者 person 的父类。
        // 注意！在存入数据的数据的有一些 不一样。
        // 因为添加元素的时候，我们知道 list 引用的对象肯定是 Person 或者 Person父类 的集合。
        //我们能够确定此时存储的元素的最小粒度比Person小的，或者Person本身都可以。
        // 但是 Person的父类不可以。

        // 读取
        Student student = arrayList.get(0);
        // 报错的原因：我们无法确定读取的是那个子类。

        // 但是下面这行代码为什么不能通过，里面的元素不都是Person 和 Person的子类吗？
        Person person = arrayList.get(0);// 哪怕我们 arrayList2 引用的是 Person的父类 也是一样的结果。
        Person person1 = arrayList2.get(0);
        // 这是因为 用下界通配符修饰的容器对象不可以直接调用get方法,
        // 这恰与上界通配符相反.除非你用Object类型的引用指向它,这也正是编译器的看法但是这种做法没有意义.
        // 因为你还是无法知道它是一个什么类型对象,它的一切方法,包括继承过来的方法还是特有的方法都无法调用,
        // 除了Object对象的几个方法.它已经完全失去了一切身份信息.
        // 造成这种情况的原因是,执行get()方法时,编译器只知道获得的元素是基类类型或是它的超类类型.
        // 但并不知道应是哪种具体类型,不同于上界通配符的做法,它不能视之为基类类型而进行向下的类型转换,
        // 而进行向上的类型转换又无法确定这个类型是否真的是你获得的类型的超类类型,
        // 除非你一直向上转换成Object类型.这也就是为何只有用Object类型引用才能指向get()方法获得的对象的原因

        Object o = arrayList.get(0);
    }


    public static void main6(String[] args) {
        ArrayList<Integer> arrayList1 = new ArrayList<>();// 指定类型 Integer
        ArrayList<Double> arrayList2 = new ArrayList<>();// 指定类型 Double
        List<? extends Number> list = arrayList1;
       //因为list 是 arrayList1 和 arrayList2 的 父类
       // 所以 list 可以引用 arrayList1 和 arrayList2的引用/对象 。
        List<? extends Number> list1 = arrayList1;
        list1 = arrayList2;

        // 那么问题就来了：既然 list 可以使用不同子类的 引用 / 对象。
        // 那么，list 在存入数据的时候，编译器能知道 add的数据是什么吗？
//        list.add(0,1);
//        list.add(0,1.0);
        // 由结果显示：是不能的。
        // 编译器是无法确定你的具体类型的。编译器为了安全起见，不建议你写入。
        // 因此得出结论：通配符的上界 不适合写入数据。

        // 通配符的上界 适合读取数据
        // 需要注意的是 用来接收读取数据的 类型 要是 上界/父类
        Number n= list.get(0);
//        Integer i= list.get(0);
        //因为 list 引用子类太多了，所以无法使用更准确的类型来接收

    }

    public static void main5(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        Alg1.print1(list1);
        System.out.println("============");
        Alg1.print2(list1);
    }

    public static void main4(String[] args) {
        Alg<Integer> alg = new Alg<>();
        System.out.println(alg);
        Alg<Integer> alg1 = new Alg<>();
        System.out.println(alg1);
    }

    public static void main3(String[] args) {
        Integer[] array = {1,2,12,4,5};
        System.out.println(Alg.<Integer>findMax(array));

    }


    public static void main2(String[] args) {
//        MyArray<String> myArray = new MyArray<>();
        MyArray<Integer> myArray1 = new MyArray<>();
        MyArray<Number> myArray2 = new MyArray<>();
    }

    public static void main1(String[] args) {
        // 裸类型
//        MyArray myArray5 = new MyArray<String>();
//        myArray5.set(0,"hello");
//        myArray5.set(1,10);
//        myArray5.set(2,'c');



//        MyArray<String> myArray = new MyArray<String>();
//        myArray.set(0,"hello");
//        String str = myArray.get(0);
//        MyArray<Integer> myArray1 = new MyArray<>();
//        myArray1.set(0,10);
//        int i = myArray1.get(0);
//        MyArray<Character> myArray2 = new MyArray<>();
//        myArray2.set(0,'c');
//        char ch = myArray2.get(0);

    }
}

